// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/memory/memory_pressure_listener.h"

#include "base/lazy_instance.h"
#include "base/observer_list_threadsafe.h"
#include "base/trace_event/trace_event.h"

namespace base {

namespace {

    class MemoryPressureObserver {
    public:
        MemoryPressureObserver()
            : async_observers_(new ObserverListThreadSafe<MemoryPressureListener>)
            , sync_observers_(new ObserverList<MemoryPressureListener>)
        {
        }

        void AddObserver(MemoryPressureListener* listener, bool sync)
        {
            async_observers_->AddObserver(listener);
            if (sync) {
                AutoLock lock(sync_observers_lock_);
                sync_observers_->AddObserver(listener);
            }
        }

        void RemoveObserver(MemoryPressureListener* listener)
        {
            async_observers_->RemoveObserver(listener);
            AutoLock lock(sync_observers_lock_);
            sync_observers_->RemoveObserver(listener);
        }

        void Notify(MemoryPressureListener::MemoryPressureLevel
                memory_pressure_level)
        {
            async_observers_->Notify(FROM_HERE,
                &MemoryPressureListener::Notify, memory_pressure_level);
            AutoLock lock(sync_observers_lock_);
            //     FOR_EACH_OBSERVER(MemoryPressureListener, *sync_observers_,
            //         MemoryPressureListener::SyncNotify(memory_pressure_level));
            DebugBreak();
        }

    private:
        scoped_refptr<ObserverListThreadSafe<MemoryPressureListener>>
            async_observers_;
        ObserverList<MemoryPressureListener>* sync_observers_;
        Lock sync_observers_lock_;

        DISALLOW_COPY_AND_ASSIGN(MemoryPressureObserver);
    };

    LazyInstance<MemoryPressureObserver>::Leaky g_observer = LAZY_INSTANCE_INITIALIZER;

    subtle::Atomic32 g_notifications_suppressed = 0;

} // namespace

MemoryPressureListener::MemoryPressureListener(
    const MemoryPressureListener::MemoryPressureCallback& callback)
    : callback_(callback)
{
    g_observer.Get().AddObserver(this, false);
}

MemoryPressureListener::MemoryPressureListener(
    const MemoryPressureListener::MemoryPressureCallback& callback,
    const MemoryPressureListener::SyncMemoryPressureCallback&
        sync_memory_pressure_callback)
    : callback_(callback)
    , sync_memory_pressure_callback_(sync_memory_pressure_callback)
{
    g_observer.Get().AddObserver(this, true);
}

MemoryPressureListener::~MemoryPressureListener()
{
    g_observer.Get().RemoveObserver(this);
}

void MemoryPressureListener::Notify(MemoryPressureLevel memory_pressure_level)
{
    callback_.Run(memory_pressure_level);
}

void MemoryPressureListener::SyncNotify(
    MemoryPressureLevel memory_pressure_level)
{
    if (!sync_memory_pressure_callback_.is_null()) {
        sync_memory_pressure_callback_.Run(memory_pressure_level);
    }
}

// static
void MemoryPressureListener::NotifyMemoryPressure(
    MemoryPressureLevel memory_pressure_level)
{
    DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);
    TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("memory-infra"),
        "MemoryPressureListener::NotifyMemoryPressure",
        TRACE_EVENT_SCOPE_THREAD, "level",
        memory_pressure_level);
    if (AreNotificationsSuppressed())
        return;
    DoNotifyMemoryPressure(memory_pressure_level);
}

// static
bool MemoryPressureListener::AreNotificationsSuppressed()
{
    return subtle::Acquire_Load(&g_notifications_suppressed) == 1;
}

// static
void MemoryPressureListener::SetNotificationsSuppressed(bool suppress)
{
    subtle::Release_Store(&g_notifications_suppressed, suppress ? 1 : 0);
}

// static
void MemoryPressureListener::SimulatePressureNotification(
    MemoryPressureLevel memory_pressure_level)
{
    // Notify all listeners even if regular pressure notifications are suppressed.
    DoNotifyMemoryPressure(memory_pressure_level);
}

// static
void MemoryPressureListener::DoNotifyMemoryPressure(
    MemoryPressureLevel memory_pressure_level)
{
    DCHECK_NE(memory_pressure_level, MEMORY_PRESSURE_LEVEL_NONE);

    g_observer.Get().Notify(memory_pressure_level);
}

} // namespace base
